This notebook will introduce several different types of graphs you can make with the ggplot package for r. The data we will use are historical U.S. Census microdata (1900-2000) from the IPUMS project. Since I am not permitted to redistribute the raw data, the data files included with this notebook are aggregated in various ways. The file ipumsdata.txt is the codebook for the raw data, and contains the information you would need to request the raw data from IPUMS, where they are free and publicly available. I have also included the code for aggregating.
These examples will graph two things: marital status by sex over time for the United States as a whole (column graphs, area graphs, line graphs, and heat maps) and sex ratios for each state over time (choropleth maps, scatterplots, and Cleveland dot plots). You will need to install the following packages: tidyverse, tigris, sf, viridis, cowplot.
Load required packages.
This block of code aggregates the raw data. Skip it if you don’t have the raw data.
If you do not have the raw data, run the following to load the aggregated data frames.
Marital Status by Sex in the United States, 1900-2000
Column Graphs
readRDS("graphs/stacked_column.RDS")

This stacked column graph shows the number of people in each marital status category (single, married, divorced, widowed) at each census between 1900 and 2000. There are four variables in this graph: the x-axis represents the year of the census, the y-axis represents the number of people in each marital status category in each year, the fill represents marital status, and the graph is faceted by sex. Three of these variables are categorical: sex, marital status, and year. Year looks like a continuous variable, and r will treat it that way, but it is actually categorical because we only have data at ten-year intervals.
To make this graph, we need to aggregate the data by all of the categorical variables that we will use for graphing: sex, year, and marital status. The marital_by_sex data frame is already aggregated appropriately:
head(marital_by_sex)
The data file is in the tidy format, which means that there is one column for each variable (YEAR, SEX, MARITAL) and one row for each value of each variable (one row for single men in 1900, one row for married men in 1900, etc.). The variable n indicates the number of people in each year/sex/marital status category; the variable TOTAL represents the number of people in each year/sex category. We don’t need it for this graph, but will use it later.
This data frame is ready to plot! For a stacked column graph, we will use the geom_col() geometry.
ggplot(marital_by_sex, aes(x = YEAR, y = n, fill = MARITAL)) +
geom_col() +
facet_grid(rows = vars(SEX)) +
scale_x_continuous(breaks = seq(1900, 2000, 20)) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Year", y = "Number of People", fill = "Marital Status") +
theme_minimal(base_size = 20)

If we want to show the percent of people in each marital status category, rather than the number of people, we just need to add the position = "fill" option in geom_col().
ggplot(marital_by_sex, aes(x = YEAR, y = n, fill = MARITAL)) +
geom_col(position = "fill") +
facet_grid(rows = vars(SEX)) +
scale_x_continuous(breaks = seq(1900, 2000, 20)) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Year", y = "Percent of People", fill = "Marital Status") +
theme_minimal(base_size = 20)

To plot the columns next to one another rather than stacking them, use the position = "dodge" option in geom_col().
ggplot(marital_by_sex, aes(x = YEAR, y = n, fill = MARITAL)) +
geom_col(position = "dodge") +
facet_grid(rows = vars(SEX)) +
scale_x_continuous(breaks = seq(1900, 2000, 20)) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Year", y = "Number of People", fill = "Marital Status") +
theme_minimal(base_size = 20)

Area Graphs
readRDS("graphs/area.RDS")

This area graph shows the number of people in each marital status category at each age in 1900, 1950, and 2000. Now there are two continuous variables (age, number of people) and three categorical variables (year, sex, marital status). The data frame marital_by_sex_age is aggregated by age, year, sex, and marital status (one row for each unique combination of age, year, sex, and marital status). The n column gives the number of people in each year/sex/age/marital status category and the TOTAL column gives the number of people in each year/sex/age category.
head(marital_by_sex_age)
The process for graphing is identical to that for a column graph, but it uses geom_area() instead of geom_col. Area graphs are good for graphs with continuous x-axis variables, whereas column graphs are better for graphs with categorical x-axis variables.
ggplot(marital_by_sex_age, aes(x = AGE, y = n, fill = MARITAL)) +
geom_area() +
facet_grid(rows = vars(YEAR), cols = vars(SEX)) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Age", y = "Number of People", fill = "Marital") +
theme_minimal(base_size = 40)

We can graph the percent of people at each age in each marital status category rather than the number by using the positon = "fill" option in geom_area().
ggplot(marital_by_sex_age, aes(x = AGE, y = n, fill = MARITAL)) +
geom_area(position = "fill") +
facet_grid(rows = vars(YEAR), cols = vars(SEX)) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Age", y = "percent of People", fill = "Marital") +
theme_minimal(base_size = 40)

Line Graphs
readRDS("graphs/line_sex.RDS")

Line graphs are good for comparing rates. This one shows the percent of women and men at each age who are in each marital status category, allowing us to easily compare the prevalence of each status at each age between women and men. This graph uses the same data frame we used above for the area graphs, but now we will use the TOTAL column to calculate percentages.
We use geom_line() rather than geom_area() and the color aesthetic rather than fill. We need to calculate the percentages ourselves (y = n/TOTAL) because the denominator is different for each line in each facet.
ggplot(marital_by_sex_age, aes(x = AGE, y = n/TOTAL, color = SEX)) +
geom_line() +
facet_grid(rows = vars(YEAR), cols = vars(MARITAL)) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Age", y = "Percent of People", color = "Sex") +
theme_minimal(base_size = 40)

Alternatively, we can use line color to indicate year, so we can see change over time within each sex and marital status category. The only difference in the code is that we have reversed the positions of sex and year.
ggplot(marital_by_sex_age, aes(x = AGE, y = n/TOTAL, color = factor(YEAR))) +
geom_line() +
facet_grid(rows = vars(SEX), cols = vars(MARITAL)) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Marital Status by Sex, 1900-2000",
x = "Age", y = "Percent of People", color = "Year") +
theme_minimal(base_size = 40)

Heat Map
readRDS("graphs/heat_map.RDS")

A heat map uses color to represent quantity. Here the quantity we are representing is the percent of men and women in each marital status category in each year. Conceptually, this is identical to the percent column graph above. We use the same data frame we used for the column graph, but now we use the TOTAL variable to calculate percentages. In a heat map, the x-axis and y-axis variables are both categorical and the fill variable is continuous. Below, I have used scale_fill_gradient() to control the color ramp.
ggplot(marital_by_sex, aes(x = SEX, y = MARITAL, fill = n/TOTAL)) +
geom_tile() +
facet_wrap(vars(YEAR)) +
scale_fill_gradient(low = "white", high = "steelblue", labels = scales::percent) +
labs(title = "Marital Status by Sex, 1900-2000", x = "", y = "") +
theme_minimal(base_size = 20) +
theme(legend.position = "none")

Sex Ratio by State
Choropleth Map
readRDS("graphs/choropleth.RDS")

A choropleth map is similar to a heat map, but it locates quantities in geographic space. This map uses color to indicate the sex ratio (number of men per 100 women) in each state in 1900 and 2000. First, we need a data frame with the sex ratio for each state in each year, which we have in sex_ratio. The STATEFIP column gives the FIPS code for the state.
head(sex_ratio)
To map these data, we need a data frame with the geometry of each state. We get this from the tigris package, using the states() function with the class = "sf" option.
states <- states(class = "sf") %>%
filter(!NAME %in% c("Hawaii", "Alaska")) %>%
mutate(STATEFIP = as.numeric(STATEFP))
This produces a simple features data frame, which has a row for each areal unit (states in this case) and a geometry column to indicate where the boundaries of each unit are.
head(states)
Simple feature collection with 6 features and 15 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -97.23909 ymin: 24.39631 xmax: -71.08857 ymax: 49.38436
epsg (SRID): 4269
proj4string: +proj=longlat +datum=NAD83 +no_defs
REGION DIVISION STATEFP STATENS GEOID STUSPS NAME LSAD MTFCC FUNCSTAT ALAND
1 3 5 54 01779805 54 WV West Virginia 00 G4000 A 62265597146
2 3 5 12 00294478 12 FL Florida 00 G4000 A 138924199212
3 2 3 17 01779784 17 IL Illinois 00 G4000 A 143788697679
4 2 4 27 00662849 27 MN Minnesota 00 G4000 A 206232257655
5 3 5 24 01714934 24 MD Maryland 00 G4000 A 25147754905
6 1 1 44 01219835 44 RI Rhode Island 00 G4000 A 2677898725
AWATER INTPTLAT INTPTLON STATEFIP geometry
1 489902816 +38.6472854 -080.6183274 54 MULTIPOLYGON (((-81.74725 3...
2 31386038155 +28.4574302 -082.4091478 12 MULTIPOLYGON (((-82.98624 2...
3 6206693598 +40.1028754 -089.1526108 17 MULTIPOLYGON (((-91.18529 4...
4 18929176411 +46.3158148 -094.1996628 27 MULTIPOLYGON (((-96.78438 4...
5 6983312282 +38.9466584 -076.6744939 24 MULTIPOLYGON (((-77.45881 3...
6 1323551636 +41.5974187 -071.5272723 44 MULTIPOLYGON (((-71.67264 4...
We then join this data frame to the sex_ratio data frame. Since both data frames have a STATEFIP column, that will be used for the join by default. In ggplot(), geom_sf() (from the sf package) draws the states. The only aesthetic we need is fill, which goes inside geom_sf(). Below I have used the viridis package for the color ramp and theme_map() from the cowplot package to remove the axes and gridlines.
inner_join(states, filter(sex_ratio, YEAR %in% c(1900, 2000))) %>%
ggplot() +
geom_sf(aes(fill = RATIO)) +
facet_grid(rows = vars(YEAR)) +
scale_fill_viridis(direction = -1) +
theme_map(base_size = 20) +
coord_sf(datum = NA) +
labs(title = "Sex Ratio by State", fill = "M/F*100")
Joining, by = "STATEFIP"

Scatter Plot
A scatter plot allows you to identify relationships between two continuous variables, using points at the intersection of the two variables, which are on the x-axis and y-axis. In this case, we will treat year as a continuous variable for visualization purposes. For the scatter plot, we will use the same data frame we used for the choropleth map, and we will join it to the states data frame to get the names of the states. We will use geom_point() to visualize data as points with color indicating state. If we wanted to, we could change the size of the points.
inner_join(states, sex_ratio) %>%
ggplot(aes(x = YEAR, y = RATIO, color = NAME)) +
geom_point() +
theme_minimal(base_size = 20) +
theme(legend.position = "bottom") +
labs(title = "Sex Ratio by State, 1900-2000",
x = "Year", y = "Sex Ratio", color = "State")
Joining, by = "STATEFIP"

If we don’t want all of the points plotting on top of each other, we can use geom_jitter() to add just a bit of randomness to spread them out enough to see them better.
inner_join(states, sex_ratio) %>%
ggplot(aes(x = YEAR, y = RATIO, color = NAME)) +
geom_jitter() +
theme_minimal(base_size = 20) +
theme(legend.position = "bottom") +
labs(title = "Sex Ratio by State, 1900-2000",
x = "Year", y = "Sex Ratio", color = "State")
Joining, by = "STATEFIP"

Cleveland Dot Plot
A Cleveland Dot Plot is a very simple scatter plot, and is generally used with a categorical x-axis variable. We will use the same data as the scatter plot, but we will facet by state in order to show how the sex ratio of each state has changed across the twentieth century. We will use the geom_point() geometry, just as we did in the first scatter plot. In a Cleveland Dot Plot, it is traditional to make the points a bit bigger than they would be in a scatter plot.
inner_join(states, sex_ratio) %>%
ggplot(aes(x = YEAR, y = RATIO, color = NAME)) +
geom_point(size = 2) +
scale_x_continuous(breaks = seq(1900, 1990, 30)) +
facet_wrap(vars(NAME)) +
theme_minimal(base_size = 20) +
theme(legend.position = "none") +
labs(title = "Sex Ratio by State, 1900-2000",
x = "Year", y = "Sex Ratio")
Joining, by = "STATEFIP"

LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90IC0gUGFydCAyIgphdXRob3I6ICJFbWlseSBLbGFuY2hlciBNZXJjaGFudCAoZWttZXJjaGFudEB1Y2RhdmlzLmVkdSkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRoaXMgbm90ZWJvb2sgd2lsbCBpbnRyb2R1Y2Ugc2V2ZXJhbCBkaWZmZXJlbnQgdHlwZXMgb2YgZ3JhcGhzIHlvdSBjYW4gbWFrZSB3aXRoIHRoZSBgZ2dwbG90YCBwYWNrYWdlIGZvciBgcmAuIFRoZSBkYXRhIHdlIHdpbGwgdXNlIGFyZSBoaXN0b3JpY2FsIFUuUy4gQ2Vuc3VzIG1pY3JvZGF0YSAoMTkwMC0yMDAwKSBmcm9tIHRoZSBbSVBVTVNdKGh0dHA6Ly9pcHVtcy5vcmcpIHByb2plY3QuIFNpbmNlIEkgYW0gbm90IHBlcm1pdHRlZCB0byByZWRpc3RyaWJ1dGUgdGhlIHJhdyBkYXRhLCB0aGUgZGF0YSBmaWxlcyBpbmNsdWRlZCB3aXRoIHRoaXMgbm90ZWJvb2sgYXJlIGFnZ3JlZ2F0ZWQgaW4gdmFyaW91cyB3YXlzLiBUaGUgZmlsZSBgaXB1bXNkYXRhLnR4dGAgaXMgdGhlIGNvZGVib29rIGZvciB0aGUgcmF3IGRhdGEsIGFuZCBjb250YWlucyB0aGUgaW5mb3JtYXRpb24geW91IHdvdWxkIG5lZWQgdG8gcmVxdWVzdCB0aGUgcmF3IGRhdGEgZnJvbSBbSVBVTVNdKGh0dHA6Ly9pcHVtcy5vcmcpLCB3aGVyZSB0aGV5IGFyZSBmcmVlIGFuZCBwdWJsaWNseSBhdmFpbGFibGUuIEkgaGF2ZSBhbHNvIGluY2x1ZGVkIHRoZSBjb2RlIGZvciBhZ2dyZWdhdGluZy4KClRoZXNlIGV4YW1wbGVzIHdpbGwgZ3JhcGggdHdvIHRoaW5nczogbWFyaXRhbCBzdGF0dXMgYnkgc2V4IG92ZXIgdGltZSBmb3IgdGhlIFVuaXRlZCBTdGF0ZXMgYXMgYSB3aG9sZSAoY29sdW1uIGdyYXBocywgYXJlYSBncmFwaHMsIGxpbmUgZ3JhcGhzLCBhbmQgaGVhdCBtYXBzKSBhbmQgc2V4IHJhdGlvcyBmb3IgZWFjaCBzdGF0ZSBvdmVyIHRpbWUgKGNob3JvcGxldGggbWFwcywgc2NhdHRlcnBsb3RzLCBhbmQgQ2xldmVsYW5kIGRvdCBwbG90cykuIFlvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCB0aGUgZm9sbG93aW5nIHBhY2thZ2VzOiBgdGlkeXZlcnNlYCwgYHRpZ3Jpc2AsIGBzZmAsIGB2aXJpZGlzYCwgYGNvd3Bsb3RgLgoKTG9hZCByZXF1aXJlZCBwYWNrYWdlcy4KYGBge3IgcmVzdWx0cyA9ICJoaWRlIn0KbGlicmFyeSh0aWR5dmVyc2UpCiNUaGUgZm9sbG93aW5nIGFyZSBuZWVkZWQgb25seSBmb3IgY2hvcm9wbGV0aCBtYXBzOgpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShzZikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KGNvd3Bsb3QpCmBgYAoKVGhpcyBibG9jayBvZiBjb2RlIGFnZ3JlZ2F0ZXMgdGhlIHJhdyBkYXRhLiBTa2lwIGl0IGlmIHlvdSBkb24ndCBoYXZlIHRoZSByYXcgZGF0YS4KYGBge3IgcmVzdWx0cyA9ICJoaWRlIn0KI0ZpbGwgaW4gcGF0aCB0byByYXcgZGF0YSBmaWxlLCB3aGljaCBzaG91bGQgYmUgaW4gLmNzdiBmb3JtYXQKaXB1bXNkYXRhIDwtICJpcHVtc2RhdGEuY3N2IgppcHVtcyA8LSByZWFkX2NzdihpcHVtc2RhdGEpICU+JSBmaWx0ZXIoQUdFIDwgOTApICU+JQogICAgICAgICAgICBtdXRhdGUoTUFSSVRBTCA9IGZhY3RvcihpZmVsc2UoTUFSU1QgJWluJSAyOjQsIDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShNQVJTVCA9PSA2LCAwLCBNQVJTVCkpLAogICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJTaW5nbGUiLCAiTWFycmllZCIsICJEaXZvcmNlZCIsICJXaWRvd2VkIikpLAogICAgICAgICAgICAgICAgICBTRVggPSBmYWN0b3IoU0VYLCBsYWJlbHMgPSBjKCJNYWxlIiwgIkZlbWFsZSIpKSkKbWFyaXRhbF9ieV9zZXggPC0gaXB1bXMgJT4lIGdyb3VwX2J5KFlFQVIsIFNFWCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50KE1BUklUQUwsIHd0ID0gUEVSV1QpICU+JSBtdXRhdGUoVE9UQUwgPSBzdW0obikpCm1hcml0YWxfYnlfc2V4X2FnZSA8LSBpcHVtcyAlPiUgZmlsdGVyKFlFQVIgJWluJSBjKDE5MDAsIDE5NTAsIDIwMDApKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShZRUFSLCBTRVgsIEFHRSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50KE1BUklUQUwsIHd0ID0gUEVSV1QpICU+JSBtdXRhdGUoVE9UQUwgPSBzdW0obikpCnNleF9yYXRpbyA8LSBpcHVtcyAlPiUgY291bnQoWUVBUiwgU1RBVEVGSVAsIFNFWCkgJT4lIAogICAgICAgICAgICAgICAgc3ByZWFkKFNFWCwgbikgJT4lIG11dGF0ZShSQVRJTyA9IE1hbGUvRmVtYWxlICogMTAwKQpzYXZlUkRTKG1hcml0YWxfYnlfc2V4LCAiZGF0YS9tYXJpdGFsX2J5X3NleC5SRFMiKQpzYXZlUkRTKG1hcml0YWxfYnlfc2V4X2FnZSwgImRhdGEvbWFyaXRhbF9ieV9zZXhfYWdlLlJEUyIpCnNhdmVSRFMoc2V4X3JhdGlvLCAiZGF0YS9zZXhfcmF0aW8uUkRTIikKYGBgCklmIHlvdSBkbyBub3QgaGF2ZSB0aGUgcmF3IGRhdGEsIHJ1biB0aGUgZm9sbG93aW5nIHRvIGxvYWQgdGhlIGFnZ3JlZ2F0ZWQgZGF0YSBmcmFtZXMuCmBgYHtyIHJlc3VsdHMgPSAiaGlkZSJ9Cm1hcml0YWxfYnlfc2V4IDwtIHJlYWRSRFMoImRhdGEvbWFyaXRhbF9ieV9zZXguUkRTIikKbWFyaXRhbF9ieV9zZXhfYWdlIDwtIHJlYWRSRFMoImRhdGEvbWFyaXRhbF9ieV9zZXhfYWdlLlJEUyIpCnNleF9yYXRpbyA8LSByZWFkUkRTKCJkYXRhL3NleF9yYXRpby5SRFMiKQpgYGAKIyBNYXJpdGFsIFN0YXR1cyBieSBTZXggaW4gdGhlIFVuaXRlZCBTdGF0ZXMsIDE5MDAtMjAwMAojIyBDb2x1bW4gR3JhcGhzCmBgYHtyIGZpZy5oZWlnaHQ9MTB9CnJlYWRSRFMoImdyYXBocy9zdGFja2VkX2NvbHVtbi5SRFMiKQpgYGAKVGhpcyBzdGFja2VkIGNvbHVtbiBncmFwaCBzaG93cyB0aGUgbnVtYmVyIG9mIHBlb3BsZSBpbiBlYWNoIG1hcml0YWwgc3RhdHVzIGNhdGVnb3J5IChzaW5nbGUsIG1hcnJpZWQsIGRpdm9yY2VkLCB3aWRvd2VkKSBhdCBlYWNoIGNlbnN1cyBiZXR3ZWVuIDE5MDAgYW5kIDIwMDAuIFRoZXJlIGFyZSBmb3VyIHZhcmlhYmxlcyBpbiB0aGlzIGdyYXBoOiB0aGUgKngtYXhpcyogcmVwcmVzZW50cyB0aGUgeWVhciBvZiB0aGUgY2Vuc3VzLCB0aGUgKnktYXhpcyogcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIHBlb3BsZSBpbiBlYWNoIG1hcml0YWwgc3RhdHVzIGNhdGVnb3J5IGluIGVhY2ggeWVhciwgdGhlICpmaWxsKiByZXByZXNlbnRzIG1hcml0YWwgc3RhdHVzLCBhbmQgdGhlIGdyYXBoIGlzICpmYWNldGVkKiBieSBzZXguIFRocmVlIG9mIHRoZXNlIHZhcmlhYmxlcyBhcmUgY2F0ZWdvcmljYWw6IHNleCwgbWFyaXRhbCBzdGF0dXMsIGFuZCB5ZWFyLiBZZWFyIGxvb2tzIGxpa2UgYSBjb250aW51b3VzIHZhcmlhYmxlLCBhbmQgYHJgIHdpbGwgdHJlYXQgaXQgdGhhdCB3YXksIGJ1dCBpdCBpcyBhY3R1YWxseSBjYXRlZ29yaWNhbCBiZWNhdXNlIHdlIG9ubHkgaGF2ZSBkYXRhIGF0IHRlbi15ZWFyIGludGVydmFscy4KClRvIG1ha2UgdGhpcyBncmFwaCwgd2UgbmVlZCB0byBhZ2dyZWdhdGUgdGhlIGRhdGEgYnkgYWxsIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgdGhhdCB3ZSB3aWxsIHVzZSBmb3IgZ3JhcGhpbmc6IHNleCwgeWVhciwgYW5kIG1hcml0YWwgc3RhdHVzLiBUaGUgYG1hcml0YWxfYnlfc2V4YCBkYXRhIGZyYW1lIGlzIGFscmVhZHkgYWdncmVnYXRlZCBhcHByb3ByaWF0ZWx5OgpgYGB7cn0KaGVhZChtYXJpdGFsX2J5X3NleCkKYGBgClRoZSBkYXRhIGZpbGUgaXMgaW4gdGhlICp0aWR5KiBmb3JtYXQsIHdoaWNoIG1lYW5zIHRoYXQgdGhlcmUgaXMgb25lIGNvbHVtbiBmb3IgZWFjaCB2YXJpYWJsZSAoYFlFQVJgLCBgU0VYYCwgYE1BUklUQUxgKSBhbmQgb25lIHJvdyBmb3IgZWFjaCB2YWx1ZSBvZiBlYWNoIHZhcmlhYmxlIChvbmUgcm93IGZvciBzaW5nbGUgbWVuIGluIDE5MDAsIG9uZSByb3cgZm9yIG1hcnJpZWQgbWVuIGluIDE5MDAsIGV0Yy4pLiBUaGUgdmFyaWFibGUgYG5gIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIHBlb3BsZSBpbiBlYWNoIHllYXIvc2V4L21hcml0YWwgc3RhdHVzIGNhdGVnb3J5OyB0aGUgdmFyaWFibGUgYFRPVEFMYCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggeWVhci9zZXggY2F0ZWdvcnkuIFdlIGRvbid0IG5lZWQgaXQgZm9yIHRoaXMgZ3JhcGgsIGJ1dCB3aWxsIHVzZSBpdCBsYXRlci4KClRoaXMgZGF0YSBmcmFtZSBpcyByZWFkeSB0byBwbG90ISBGb3IgYSBzdGFja2VkIGNvbHVtbiBncmFwaCwgd2Ugd2lsbCB1c2UgdGhlIGBnZW9tX2NvbCgpYCBnZW9tZXRyeS4KYGBge3IgZmlnLmhlaWdodD0xMH0KZ2dwbG90KG1hcml0YWxfYnlfc2V4LCBhZXMoeCA9IFlFQVIsIHkgPSBuLCBmaWxsID0gTUFSSVRBTCkpICsgCiAgZ2VvbV9jb2woKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTRVgpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTAwLCAyMDAwLCAyMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKwogIGxhYnModGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMgYnkgU2V4LCAxOTAwLTIwMDAiLAogICAgICAgeCA9ICJZZWFyIiwgeSA9ICJOdW1iZXIgb2YgUGVvcGxlIiwgZmlsbCA9ICJNYXJpdGFsIFN0YXR1cyIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDIwKQpgYGAKSWYgd2Ugd2FudCB0byBzaG93IHRoZSAqcGVyY2VudCogb2YgcGVvcGxlIGluIGVhY2ggbWFyaXRhbCBzdGF0dXMgY2F0ZWdvcnksIHJhdGhlciB0aGFuIHRoZSAqbnVtYmVyKiBvZiBwZW9wbGUsIHdlIGp1c3QgbmVlZCB0byBhZGQgdGhlIGBwb3NpdGlvbiA9ICJmaWxsImAgb3B0aW9uIGluIGBnZW9tX2NvbCgpYC4KYGBge3IgZmlnLmhlaWdodD0xMH0KZ2dwbG90KG1hcml0YWxfYnlfc2V4LCBhZXMoeCA9IFlFQVIsIHkgPSBuLCBmaWxsID0gTUFSSVRBTCkpICsgCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZmlsbCIpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNFWCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5MDAsIDIwMDAsIDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsKICBsYWJzKHRpdGxlID0gIk1hcml0YWwgU3RhdHVzIGJ5IFNleCwgMTkwMC0yMDAwIiwKICAgICAgIHggPSAiWWVhciIsIHkgPSAiUGVyY2VudCBvZiBQZW9wbGUiLCBmaWxsID0gIk1hcml0YWwgU3RhdHVzIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMjApCmBgYApUbyBwbG90IHRoZSBjb2x1bW5zIG5leHQgdG8gb25lIGFub3RoZXIgcmF0aGVyIHRoYW4gc3RhY2tpbmcgdGhlbSwgdXNlIHRoZSBgcG9zaXRpb24gPSAiZG9kZ2UiYCBvcHRpb24gaW4gYGdlb21fY29sKClgLgpgYGB7ciBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QobWFyaXRhbF9ieV9zZXgsIGFlcyh4ID0gWUVBUiwgeSA9IG4sIGZpbGwgPSBNQVJJVEFMKSkgKyAKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNFWCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5MDAsIDIwMDAsIDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgbGFicyh0aXRsZSA9ICJNYXJpdGFsIFN0YXR1cyBieSBTZXgsIDE5MDAtMjAwMCIsCiAgICAgICB4ID0gIlllYXIiLCB5ID0gIk51bWJlciBvZiBQZW9wbGUiLCBmaWxsID0gIk1hcml0YWwgU3RhdHVzIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMjApCmBgYAojIyBBcmVhIEdyYXBocwpgYGB7ciBmaWcuaGVpZ2h0PTIwfQpyZWFkUkRTKCJncmFwaHMvYXJlYS5SRFMiKQpgYGAKVGhpcyBhcmVhIGdyYXBoIHNob3dzIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggbWFyaXRhbCBzdGF0dXMgY2F0ZWdvcnkgYXQgZWFjaCBhZ2UgaW4gMTkwMCwgMTk1MCwgYW5kIDIwMDAuIE5vdyB0aGVyZSBhcmUgdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzIChhZ2UsIG51bWJlciBvZiBwZW9wbGUpIGFuZCB0aHJlZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKHllYXIsIHNleCwgbWFyaXRhbCBzdGF0dXMpLiBUaGUgZGF0YSBmcmFtZSBgbWFyaXRhbF9ieV9zZXhfYWdlYCBpcyBhZ2dyZWdhdGVkIGJ5IGFnZSwgeWVhciwgc2V4LCBhbmQgbWFyaXRhbCBzdGF0dXMgKG9uZSByb3cgZm9yIGVhY2ggdW5pcXVlIGNvbWJpbmF0aW9uIG9mIGFnZSwgeWVhciwgc2V4LCBhbmQgbWFyaXRhbCBzdGF0dXMpLiBUaGUgYG5gIGNvbHVtbiBnaXZlcyB0aGUgbnVtYmVyIG9mIHBlb3BsZSBpbiBlYWNoIHllYXIvc2V4L2FnZS9tYXJpdGFsIHN0YXR1cyBjYXRlZ29yeSBhbmQgdGhlIGBUT1RBTGAgY29sdW1uIGdpdmVzIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggeWVhci9zZXgvYWdlIGNhdGVnb3J5LgpgYGB7cn0KaGVhZChtYXJpdGFsX2J5X3NleF9hZ2UpCmBgYApUaGUgcHJvY2VzcyBmb3IgZ3JhcGhpbmcgaXMgaWRlbnRpY2FsIHRvIHRoYXQgZm9yIGEgY29sdW1uIGdyYXBoLCBidXQgaXQgdXNlcyBgZ2VvbV9hcmVhKClgIGluc3RlYWQgb2YgYGdlb21fY29sYC4gQXJlYSBncmFwaHMgYXJlIGdvb2QgZm9yIGdyYXBocyB3aXRoIGNvbnRpbnVvdXMgeC1heGlzIHZhcmlhYmxlcywgd2hlcmVhcyBjb2x1bW4gZ3JhcGhzIGFyZSBiZXR0ZXIgZm9yIGdyYXBocyB3aXRoIGNhdGVnb3JpY2FsIHgtYXhpcyB2YXJpYWJsZXMuCmBgYHtyIGZpZy5oZWlnaHQ9MjB9CmdncGxvdChtYXJpdGFsX2J5X3NleF9hZ2UsIGFlcyh4ID0gQUdFLCB5ID0gbiwgZmlsbCA9IE1BUklUQUwpKSArCiAgZ2VvbV9hcmVhKCkgKwogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoWUVBUiksIGNvbHMgPSB2YXJzKFNFWCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKwogIGxhYnModGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMgYnkgU2V4LCAxOTAwLTIwMDAiLAogICAgICAgeCA9ICJBZ2UiLCB5ID0gIk51bWJlciBvZiBQZW9wbGUiLCBmaWxsID0gIk1hcml0YWwiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA0MCkKYGBgCldlIGNhbiBncmFwaCB0aGUgKnBlcmNlbnQqIG9mIHBlb3BsZSBhdCBlYWNoIGFnZSBpbiBlYWNoIG1hcml0YWwgc3RhdHVzIGNhdGVnb3J5IHJhdGhlciB0aGFuIHRoZSAqbnVtYmVyKiBieSB1c2luZyB0aGUgYHBvc2l0b24gPSAiZmlsbCJgIG9wdGlvbiBpbiBgZ2VvbV9hcmVhKClgLgpgYGB7ciBmaWcuaGVpZ2h0PTIwfQpnZ3Bsb3QobWFyaXRhbF9ieV9zZXhfYWdlLCBhZXMoeCA9IEFHRSwgeSA9IG4sIGZpbGwgPSBNQVJJVEFMKSkgKwogIGdlb21fYXJlYShwb3NpdGlvbiA9ICJmaWxsIikgKwogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoWUVBUiksIGNvbHMgPSB2YXJzKFNFWCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgbGFicyh0aXRsZSA9ICJNYXJpdGFsIFN0YXR1cyBieSBTZXgsIDE5MDAtMjAwMCIsCiAgICAgICB4ID0gIkFnZSIsIHkgPSAicGVyY2VudCBvZiBQZW9wbGUiLCBmaWxsID0gIk1hcml0YWwiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA0MCkKYGBgCiMjIExpbmUgR3JhcGhzCmBgYHtyIGZpZy5oZWlnaHQ9MjB9CnJlYWRSRFMoImdyYXBocy9saW5lX3NleC5SRFMiKQpgYGAKTGluZSBncmFwaHMgYXJlIGdvb2QgZm9yIGNvbXBhcmluZyByYXRlcy4gVGhpcyBvbmUgc2hvd3MgdGhlIHBlcmNlbnQgb2Ygd29tZW4gYW5kIG1lbiBhdCBlYWNoIGFnZSB3aG8gYXJlIGluIGVhY2ggbWFyaXRhbCBzdGF0dXMgY2F0ZWdvcnksIGFsbG93aW5nIHVzIHRvIGVhc2lseSBjb21wYXJlIHRoZSBwcmV2YWxlbmNlIG9mIGVhY2ggc3RhdHVzIGF0IGVhY2ggYWdlIGJldHdlZW4gd29tZW4gYW5kIG1lbi4gVGhpcyBncmFwaCB1c2VzIHRoZSBzYW1lIGRhdGEgZnJhbWUgd2UgdXNlZCBhYm92ZSBmb3IgdGhlIGFyZWEgZ3JhcGhzLCBidXQgbm93IHdlIHdpbGwgdXNlIHRoZSBgVE9UQUxgIGNvbHVtbiB0byBjYWxjdWxhdGUgcGVyY2VudGFnZXMuCgpXZSB1c2UgYGdlb21fbGluZSgpYCByYXRoZXIgdGhhbiBgZ2VvbV9hcmVhKClgIGFuZCB0aGUgYGNvbG9yYCBhZXN0aGV0aWMgcmF0aGVyIHRoYW4gYGZpbGxgLiBXZSBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZXMgb3Vyc2VsdmVzIChgeSA9IG4vVE9UQUxgKSBiZWNhdXNlIHRoZSBkZW5vbWluYXRvciBpcyBkaWZmZXJlbnQgZm9yIGVhY2ggbGluZSBpbiBlYWNoIGZhY2V0LgpgYGB7ciBmaWcuaGVpZ2h0PTIwfQpnZ3Bsb3QobWFyaXRhbF9ieV9zZXhfYWdlLCBhZXMoeCA9IEFHRSwgeSA9IG4vVE9UQUwsIGNvbG9yID0gU0VYKSkgKyAKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhZRUFSKSwgY29scyA9IHZhcnMoTUFSSVRBTCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgbGFicyh0aXRsZSA9ICJNYXJpdGFsIFN0YXR1cyBieSBTZXgsIDE5MDAtMjAwMCIsCiAgICAgICB4ID0gIkFnZSIsIHkgPSAiUGVyY2VudCBvZiBQZW9wbGUiLCBjb2xvciA9ICJTZXgiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSA0MCkKYGBgCkFsdGVybmF0aXZlbHksIHdlIGNhbiB1c2UgbGluZSBjb2xvciB0byBpbmRpY2F0ZSB5ZWFyLCBzbyB3ZSBjYW4gc2VlIGNoYW5nZSBvdmVyIHRpbWUgd2l0aGluIGVhY2ggc2V4IGFuZCBtYXJpdGFsIHN0YXR1cyBjYXRlZ29yeS4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpbiB0aGUgY29kZSBpcyB0aGF0IHdlIGhhdmUgcmV2ZXJzZWQgdGhlIHBvc2l0aW9ucyBvZiBzZXggYW5kIHllYXIuCmBgYHtyIGZpZy5oZWlnaHQ9MjB9CmdncGxvdChtYXJpdGFsX2J5X3NleF9hZ2UsIGFlcyh4ID0gQUdFLCB5ID0gbi9UT1RBTCwgY29sb3IgPSBmYWN0b3IoWUVBUikpKSArIAogIGdlb21fbGluZSgpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNFWCksIGNvbHMgPSB2YXJzKE1BUklUQUwpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMgYnkgU2V4LCAxOTAwLTIwMDAiLAogICAgICAgeCA9ICJBZ2UiLCB5ID0gIlBlcmNlbnQgb2YgUGVvcGxlIiwgY29sb3IgPSAiWWVhciIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDQwKQpgYGAKIyMgSGVhdCBNYXAKYGBge3IgZmlnLmhlaWdodD0xMH0KcmVhZFJEUygiZ3JhcGhzL2hlYXRfbWFwLlJEUyIpCmBgYApBIGhlYXQgbWFwIHVzZXMgY29sb3IgdG8gcmVwcmVzZW50IHF1YW50aXR5LiBIZXJlIHRoZSBxdWFudGl0eSB3ZSBhcmUgcmVwcmVzZW50aW5nIGlzIHRoZSBwZXJjZW50IG9mIG1lbiBhbmQgd29tZW4gaW4gZWFjaCBtYXJpdGFsIHN0YXR1cyBjYXRlZ29yeSBpbiBlYWNoIHllYXIuIENvbmNlcHR1YWxseSwgdGhpcyBpcyBpZGVudGljYWwgdG8gdGhlIHBlcmNlbnQgY29sdW1uIGdyYXBoIGFib3ZlLiBXZSB1c2UgdGhlIHNhbWUgZGF0YSBmcmFtZSB3ZSB1c2VkIGZvciB0aGUgY29sdW1uIGdyYXBoLCBidXQgbm93IHdlIHVzZSB0aGUgYFRPVEFMYCB2YXJpYWJsZSB0byBjYWxjdWxhdGUgcGVyY2VudGFnZXMuIEluIGEgaGVhdCBtYXAsIHRoZSB4LWF4aXMgYW5kIHktYXhpcyB2YXJpYWJsZXMgYXJlIGJvdGggY2F0ZWdvcmljYWwgYW5kIHRoZSBmaWxsIHZhcmlhYmxlIGlzIGNvbnRpbnVvdXMuIEJlbG93LCBJIGhhdmUgdXNlZCBgc2NhbGVfZmlsbF9ncmFkaWVudCgpYCB0byBjb250cm9sIHRoZSBjb2xvciByYW1wLgpgYGB7ciBmaWcuaGVpZ2h0ID0gMTB9CmdncGxvdChtYXJpdGFsX2J5X3NleCwgYWVzKHggPSBTRVgsIHkgPSBNQVJJVEFMLCBmaWxsID0gbi9UT1RBTCkpICsKICBnZW9tX3RpbGUoKSArCiAgZmFjZXRfd3JhcCh2YXJzKFlFQVIpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gInN0ZWVsYmx1ZSIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogICAgbGFicyh0aXRsZSA9ICJNYXJpdGFsIFN0YXR1cyBieSBTZXgsIDE5MDAtMjAwMCIsIHggPSAiIiwgeSA9ICIiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAyMCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCiMgU2V4IFJhdGlvIGJ5IFN0YXRlCiMjIENob3JvcGxldGggTWFwCmBgYHtyIGZpZy5oZWlnaHQ9MTB9CnJlYWRSRFMoImdyYXBocy9jaG9yb3BsZXRoLlJEUyIpCmBgYApBIGNob3JvcGxldGggbWFwIGlzIHNpbWlsYXIgdG8gYSBoZWF0IG1hcCwgYnV0IGl0IGxvY2F0ZXMgcXVhbnRpdGllcyBpbiBnZW9ncmFwaGljIHNwYWNlLiBUaGlzIG1hcCB1c2VzIGNvbG9yIHRvIGluZGljYXRlIHRoZSBzZXggcmF0aW8gKG51bWJlciBvZiBtZW4gcGVyIDEwMCB3b21lbikgaW4gZWFjaCBzdGF0ZSBpbiAxOTAwIGFuZCAyMDAwLiBGaXJzdCwgd2UgbmVlZCBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgc2V4IHJhdGlvIGZvciBlYWNoIHN0YXRlIGluIGVhY2ggeWVhciwgd2hpY2ggd2UgaGF2ZSBpbiBgc2V4X3JhdGlvYC4gVGhlIGBTVEFURUZJUGAgY29sdW1uIGdpdmVzIHRoZSBGSVBTIGNvZGUgZm9yIHRoZSBzdGF0ZS4KYGBge3J9CmhlYWQoc2V4X3JhdGlvKQpgYGAKVG8gbWFwIHRoZXNlIGRhdGEsIHdlIG5lZWQgYSBkYXRhIGZyYW1lIHdpdGggdGhlIGdlb21ldHJ5IG9mIGVhY2ggc3RhdGUuIFdlIGdldCB0aGlzIGZyb20gdGhlIGB0aWdyaXNgIHBhY2thZ2UsIHVzaW5nIHRoZSBgc3RhdGVzKClgIGZ1bmN0aW9uIHdpdGggdGhlIGBjbGFzcyA9ICJzZiJgIG9wdGlvbi4gCmBgYHtyIHJlc3VsdHMgPSAiaGlkZSJ9CnN0YXRlcyA8LSBzdGF0ZXMoY2xhc3MgPSAic2YiKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighTkFNRSAlaW4lIGMoIkhhd2FpaSIsICJBbGFza2EiKSkgJT4lCiAgICAgICAgICAgIG11dGF0ZShTVEFURUZJUCA9IGFzLm51bWVyaWMoU1RBVEVGUCkpIApgYGAKVGhpcyBwcm9kdWNlcyBhIHNpbXBsZSBmZWF0dXJlcyBkYXRhIGZyYW1lLCB3aGljaCBoYXMgYSByb3cgZm9yIGVhY2ggYXJlYWwgdW5pdCAoc3RhdGVzIGluIHRoaXMgY2FzZSkgYW5kIGEgYGdlb21ldHJ5YCBjb2x1bW4gdG8gaW5kaWNhdGUgd2hlcmUgdGhlIGJvdW5kYXJpZXMgb2YgZWFjaCB1bml0IGFyZS4KYGBge3J9CmhlYWQoc3RhdGVzKQpgYGAKV2UgdGhlbiBqb2luIHRoaXMgZGF0YSBmcmFtZSB0byB0aGUgYHNleF9yYXRpb2AgZGF0YSBmcmFtZS4gU2luY2UgYm90aCBkYXRhIGZyYW1lcyBoYXZlIGEgYFNUQVRFRklQYCBjb2x1bW4sIHRoYXQgd2lsbCBiZSB1c2VkIGZvciB0aGUgam9pbiBieSBkZWZhdWx0LiBJbiBgZ2dwbG90KClgLCBgZ2VvbV9zZigpYCAoZnJvbSB0aGUgYHNmYCBwYWNrYWdlKSBkcmF3cyB0aGUgc3RhdGVzLiBUaGUgb25seSBhZXN0aGV0aWMgd2UgbmVlZCBpcyAqZmlsbCosIHdoaWNoIGdvZXMgaW5zaWRlIGBnZW9tX3NmKClgLiBCZWxvdyBJIGhhdmUgdXNlZCB0aGUgYHZpcmlkaXNgIHBhY2thZ2UgZm9yIHRoZSBjb2xvciByYW1wIGFuZCBgdGhlbWVfbWFwKClgIGZyb20gdGhlIGBjb3dwbG90YCBwYWNrYWdlIHRvIHJlbW92ZSB0aGUgYXhlcyBhbmQgZ3JpZGxpbmVzLgpgYGB7ciBmaWcuaGVpZ2h0PTEwfQppbm5lcl9qb2luKHN0YXRlcywgZmlsdGVyKHNleF9yYXRpbywgWUVBUiAlaW4lIGMoMTkwMCwgMjAwMCkpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBSQVRJTykpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFlFQVIpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpcmVjdGlvbiA9IC0xKSArCiAgdGhlbWVfbWFwKGJhc2Vfc2l6ZSA9IDIwKSArCiAgY29vcmRfc2YoZGF0dW0gPSBOQSkgKwogIGxhYnModGl0bGUgPSAiU2V4IFJhdGlvIGJ5IFN0YXRlIiwgZmlsbCA9ICJNL0YqMTAwIikKYGBgCiMjIFNjYXR0ZXIgUGxvdApBIHNjYXR0ZXIgcGxvdCBhbGxvd3MgeW91IHRvIGlkZW50aWZ5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0d28gY29udGludW91cyB2YXJpYWJsZXMsIHVzaW5nIHBvaW50cyBhdCB0aGUgaW50ZXJzZWN0aW9uIG9mIHRoZSB0d28gdmFyaWFibGVzLCB3aGljaCBhcmUgb24gdGhlIHgtYXhpcyBhbmQgeS1heGlzLiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgdHJlYXQgeWVhciBhcyBhIGNvbnRpbnVvdXMgdmFyaWFibGUgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMuIEZvciB0aGUgc2NhdHRlciBwbG90LCB3ZSB3aWxsIHVzZSB0aGUgc2FtZSBkYXRhIGZyYW1lIHdlIHVzZWQgZm9yIHRoZSBjaG9yb3BsZXRoIG1hcCwgYW5kIHdlIHdpbGwgam9pbiBpdCB0byB0aGUgYHN0YXRlc2AgZGF0YSBmcmFtZSB0byBnZXQgdGhlIG5hbWVzIG9mIHRoZSBzdGF0ZXMuIFdlIHdpbGwgdXNlIGBnZW9tX3BvaW50KClgIHRvIHZpc3VhbGl6ZSBkYXRhIGFzIHBvaW50cyB3aXRoIGNvbG9yIGluZGljYXRpbmcgc3RhdGUuIElmIHdlIHdhbnRlZCB0bywgd2UgY291bGQgY2hhbmdlIHRoZSBzaXplIG9mIHRoZSBwb2ludHMuCmBgYHtyIGZpZy5oZWlnaHQ9MTB9CmlubmVyX2pvaW4oc3RhdGVzLCBzZXhfcmF0aW8pICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBZRUFSLCB5ID0gUkFUSU8sIGNvbG9yID0gTkFNRSkpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMjApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGxhYnModGl0bGUgPSAiU2V4IFJhdGlvIGJ5IFN0YXRlLCAxOTAwLTIwMDAiLAogICAgICAgeCA9ICJZZWFyIiwgeSA9ICJTZXggUmF0aW8iLCBjb2xvciA9ICJTdGF0ZSIpCmBgYApJZiB3ZSBkb24ndCB3YW50IGFsbCBvZiB0aGUgcG9pbnRzIHBsb3R0aW5nIG9uIHRvcCBvZiBlYWNoIG90aGVyLCB3ZSBjYW4gdXNlIGBnZW9tX2ppdHRlcigpYCB0byBhZGQganVzdCBhIGJpdCBvZiByYW5kb21uZXNzIHRvIHNwcmVhZCB0aGVtIG91dCBlbm91Z2ggdG8gc2VlIHRoZW0gYmV0dGVyLgpgYGB7ciBmaWcuaGVpZ2h0PTEwfQppbm5lcl9qb2luKHN0YXRlcywgc2V4X3JhdGlvKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWUVBUiwgeSA9IFJBVElPLCBjb2xvciA9IE5BTUUpKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAyMCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgbGFicyh0aXRsZSA9ICJTZXggUmF0aW8gYnkgU3RhdGUsIDE5MDAtMjAwMCIsCiAgICAgICB4ID0gIlllYXIiLCB5ID0gIlNleCBSYXRpbyIsIGNvbG9yID0gIlN0YXRlIikKYGBgCiMjIENsZXZlbGFuZCBEb3QgUGxvdApBIENsZXZlbGFuZCBEb3QgUGxvdCBpcyBhIHZlcnkgc2ltcGxlIHNjYXR0ZXIgcGxvdCwgYW5kIGlzIGdlbmVyYWxseSB1c2VkIHdpdGggYSBjYXRlZ29yaWNhbCB4LWF4aXMgdmFyaWFibGUuIFdlIHdpbGwgdXNlIHRoZSBzYW1lIGRhdGEgYXMgdGhlIHNjYXR0ZXIgcGxvdCwgYnV0IHdlIHdpbGwgZmFjZXQgYnkgc3RhdGUgaW4gb3JkZXIgdG8gc2hvdyBob3cgdGhlIHNleCByYXRpbyBvZiBlYWNoIHN0YXRlIGhhcyBjaGFuZ2VkIGFjcm9zcyB0aGUgdHdlbnRpZXRoIGNlbnR1cnkuIFdlIHdpbGwgdXNlIHRoZSBgZ2VvbV9wb2ludCgpYCBnZW9tZXRyeSwganVzdCBhcyB3ZSBkaWQgaW4gdGhlIGZpcnN0IHNjYXR0ZXIgcGxvdC4gSW4gYSBDbGV2ZWxhbmQgRG90IFBsb3QsIGl0IGlzIHRyYWRpdGlvbmFsIHRvIG1ha2UgdGhlIHBvaW50cyBhIGJpdCBiaWdnZXIgdGhhbiB0aGV5IHdvdWxkIGJlIGluIGEgc2NhdHRlciBwbG90LgpgYGB7ciBmaWcuaGVpZ2h0PTEwfQppbm5lcl9qb2luKHN0YXRlcywgc2V4X3JhdGlvKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWUVBUiwgeSA9IFJBVElPLCBjb2xvciA9IE5BTUUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTkwMCwgMTk5MCwgMzApKSArCiAgZmFjZXRfd3JhcCh2YXJzKE5BTUUpKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAyMCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiU2V4IFJhdGlvIGJ5IFN0YXRlLCAxOTAwLTIwMDAiLAogICAgIHggPSAiWWVhciIsIHkgPSAiU2V4IFJhdGlvIikKYGBgCgo=